package MineGod;

import java.util.ArrayList;
import java.util.Random;


public class Chunk {
	public static final int CHUNK_HEIGHT = 40;
	public static final int CHUNK_WIDTH = 60;
	public static final int CHUNK_PIXEL_HEIGHT = (int) (Block.WIDTH * CHUNK_HEIGHT);
	public static final int CHUNK_PIXEL_WIDTH = (int) (Block.WIDTH * CHUNK_WIDTH);
	public static final int MIN_VEINS = 12;
	public static final int MAX_VEINS = 20;
	public static final int MIN_MOBS = 3;
	public static final int MAX_MOBS = 5;
	
	public ArrayList<ResidualArrayElement> rAEs = new ArrayList<ResidualArrayElement>();
	
	public Block[][] blocks;
	public final double worldPosIndex;
	public final double worldPosPixelIndex;
	public static final Random rand = new Random();
	public static final BlockGenerator[] veins = {new BlockStoneGenerator(), new BlockSandGenerator(), 
		new BlockWaterGenerator(), new BlockAirGenerator(), new BlockLavaGenerator(), new BlockHardStoneGenerator()};
	
	public Chunk(int index)
	{
		this.worldPosIndex = index;
		worldPosPixelIndex = index*CHUNK_WIDTH;
		blocks = new Block[CHUNK_WIDTH][CHUNK_HEIGHT];
		this.generate();
		
		// The first Chunk of the game needs an opening for the player
		if (index == 0){
			createStartingHole();
		}
	}
	
	public Chunk(int index, Chunk prevChunk)
	{
		this.worldPosIndex = index;
		worldPosPixelIndex = index*CHUNK_WIDTH;
		blocks = new Block[CHUNK_WIDTH][CHUNK_HEIGHT];
		this.generate(prevChunk);
	}
	
	private void generate(Chunk prevChunk) {
		//generate dirt
		for(int i = 0; i < blocks.length; i++){
			for(int j = 0; j < blocks[i].length; j++){
				blocks[i][j] = new BlockDirt();
			}
		}

		//Add residual veins
		for (ResidualArrayElement rAE: prevChunk.rAEs){
			for (int[] locs: rAE.blockLocs){
				if (locs[0] >= blocks.length){
					this.blocks[(locs[0]+Chunk.CHUNK_WIDTH)%Chunk.CHUNK_WIDTH][locs[1]] = rAE.bGen.provideBlock();
				}
			}
		}
		
		//Add new veins
		int numVeins = rand.nextInt(MAX_VEINS-MIN_VEINS+1)+MIN_VEINS;
		for (int i =0; i<numVeins; i++)
		{
			BlockGenerator bGen = veins[rand.nextInt(veins.length)];
			rAEs.add(new ResidualArrayElement(bGen, bGen.generateVein(this)));
		}
		
		//Spawn water along top of screen
		for (int i = 0; i<CHUNK_WIDTH; i++){
			blocks[i][0] = new BlockWater();
		}
		
		//Try to spawn Mobs
		int numMobs = rand.nextInt(MAX_MOBS-MIN_MOBS+1)+MIN_MOBS;
		for (int i = 0; i< numMobs; i++){
			//find suitable location and spawn mob
			int l = rand.nextInt(CHUNK_WIDTH);
			int m = rand.nextInt(CHUNK_HEIGHT);
			int[] checks = {0,1,-1,2,-2,3,-3,4,-4,5,-5,6,-6};
			boolean placed = false;
			for (int n : checks){
				for (int o : checks){
					if (!(l+o<0 || l+o>=Chunk.CHUNK_WIDTH || m+n<0 || m+n>=Chunk.CHUNK_HEIGHT)){
						Block b = blocks[l+o][m+n];
						if (b != null && !(b.isSolid || b.isLiquid))
						{
							MineGod.entities.add(MobSpawner.spawnRandomMob((l+o)*Block.WIDTH+worldPosIndex*CHUNK_PIXEL_WIDTH, (m+n)*Block.WIDTH));
							placed = true;
						}
					}
					if (placed){
						break;
					}
				}
				if (placed){
					break;
				}
			}
		}
		
		
	}



	private void generate() {
		for(int i = 0; i < blocks.length; i++){
			for(int j = 0; j < blocks[i].length; j++){
				blocks[i][j] = new BlockDirt();
			}
		}
		
		//Add veins
		int numVeins = rand.nextInt(MAX_VEINS-MIN_VEINS+1)+MIN_VEINS;
		for (int i =0; i<numVeins; i++)
		{
			BlockGenerator bGen = veins[rand.nextInt(veins.length)];
			rAEs.add(new ResidualArrayElement(bGen, bGen.generateVein(this)));
		}
		
		//Spawn water along top of screen
		for (int i = 0; i<CHUNK_WIDTH; i++){
			blocks[i][0] = new BlockWater();
		}
		
		//Don't spawn Mobs in first chunk
		/*int numMobs = rand.nextInt(MAX_MOBS-MIN_MOBS+1)+MIN_MOBS;
		
		for (int i = 0; i< numMobs; i++){
			//find suitable location and spawn mob
			int l = rand.nextInt(CHUNK_WIDTH);
			int m = rand.nextInt(CHUNK_HEIGHT);
			boolean placed = false;
			int[] checks = {0,1,-1,2,-2,3,-3,4,-4,5,-5,6,-6};
			for (int n : checks){
				for (int o : checks){
					if (!(l+o<0 || l+o>=Chunk.CHUNK_WIDTH || m+n<0 || m+n>=Chunk.CHUNK_HEIGHT)){
						Block b = blocks[l+o][m+n];
						if (b != null && !(b.isSolid || b.isLiquid))
						{
							MineGod.entities.add(MobSpawner.spawnRandomMob((l+o)*Block.WIDTH+worldPosIndex*CHUNK_PIXEL_WIDTH, (m+n)*Block.WIDTH));
							placed = true;
						}
					}
					if (placed){
						break;
					}
				}
				if (placed){
					break;
				}
			}
		}*/
		
	}
	
	/*private ArrayList<int[]> generateVein(BlockGenerator bGen) {
		int i = rand.nextInt(blocks.length);
		int j = rand.nextInt(blocks[i].length);
		
		blocks[i][j] = bGen.generate();
		ArrayList<int[]> offRightOrLeft = new ArrayList<int[]>();
		
		//Now create the vein
		for (int k = 0; k < (rand.nextInt(bGen.maxVeinLength-bGen.minVeinLength+1)+bGen.minVeinLength); k++){
			ArrayList<int[]> valid = new ArrayList<int[]>();
			for(int x = -1; x <= 1; x++) {
				for(int y = -1; y <= 1; y++) {
					if(!(x == 0 && y== 0)) {
						int checkX = i + x;
						int checkY = j + y;
						if(!(checkX < 0 || checkX >= blocks.length || checkY < 0 || checkY >= CHUNK_HEIGHT) && !(blocks[checkX][checkY].type == bGen.type)){
							valid.add(new int[] {checkX,checkY});
						}
						else if ((checkX < 0 || checkX >= blocks.length) && !(checkY < 0 || checkY >= CHUNK_HEIGHT)){
							valid.add(new int[] {checkX,checkY});
						}
					}
				}
			}
			
			if (valid.size() == 0){
				break;
			}
			int[] nextLoc = valid.get(rand.nextInt(valid.size()));
			
			
			i = nextLoc[0];
			j = nextLoc[1];
			if(!(i < 0 || j < 0 || j >= CHUNK_HEIGHT)){
				if(i >= blocks.length || i < 0) {
					offRightOrLeft.add(new int[] {i,j});
				}
				else{
					blocks[i][j] = bGen.generate();
				}
			}
		}
		return offRightOrLeft;
	}*/

	public void renderChunk(double worldPos)
	{
		
		for(int i = 0; i < CHUNK_WIDTH; i++){
			if (!(i*Block.WIDTH+Block.WIDTH + worldPosIndex*(CHUNK_PIXEL_WIDTH)-worldPos<0 || i*Block.WIDTH + worldPosIndex*(CHUNK_PIXEL_WIDTH) -Block.WIDTH-worldPos>Chunk.CHUNK_WIDTH*Block.WIDTH)){
				for(int j = 0; j < CHUNK_HEIGHT; j++){
					blocks[i][j].render(Block.WIDTH*i + worldPosIndex*(CHUNK_PIXEL_WIDTH) - worldPos, Block.WIDTH*j);
				}
			}
		}
		
	}

	
	private void createStartingHole(){
		for(int i = 0; i < CHUNK_WIDTH; i++){
			for(int j = CHUNK_HEIGHT/3; j < CHUNK_HEIGHT*2/3; j++){
				blocks[i][j] = new BlockAir();
			}
		}
		
		for(int i = 0; i < 1; i++){
			for(int j = 0; j < 1/*CHUNK_HEIGHT/3-1*/; j++){
				blocks[i][j] = new BlockWater();
			}
		}
	}
	
	
	// Take into account edge cases...
	public CollisionGroup getCollidedSideBlocks(double x, double y, double width, double height){
		BlockMask tl = new BlockMask(Utils.pointToBlock(x, y), x - x%16, y - y%16);
		BlockMask tr = new BlockMask(Utils.pointToBlock(x+width, y), (x+width) - (x+width)%16, y - y%16);
		BlockMask bl = new BlockMask(Utils.pointToBlock(x, y+height), x - x%16, (y+height) - (y+height)%16);
		BlockMask br = new BlockMask(Utils.pointToBlock(x+width, y+height), (x+width) - (x+width)%16, (y+height) - (y+height)%16);
		return new CollisionGroup(tl, tr, bl, br);
	}
	
	public CollisionGroup getCollidedLeftSideBlocks(double x, double y, double width, double height){
		BlockMask tl = new BlockMask(Utils.pointToBlock(x, y), x - x%16, y - y%16);
		BlockMask bl = new BlockMask(Utils.pointToBlock(x, y+height), x - x%16, (y+height) - (y+height)%16);
		return new CollisionGroupLeft(tl, bl);
	}
	
	public CollisionGroup getCollidedRightSideBlocks(double x, double y, double width, double height){
		BlockMask tr = new BlockMask(Utils.pointToBlock(x+width, y), (x+width) - (x+width)%16, y - y%16);
		BlockMask br = new BlockMask(Utils.pointToBlock(x+width, y+height), (x+width) - (x+width)%16, (y+height) - (y+height)%16);
		return new CollisionGroupRight(tr, br);
	}
	
}
